<template>
  <div class="position-full">
    <div v-for="(row, rIdx) in gridMatrix" :key="rIdx">
      <div
        v-for="(item, cIdx) in row" :key="cIdx"
        :style="gridItemStyle(item)" class="snap-grid-item"></div>
    </div>
    <!--	  <div v-if="hoverGridItem" :style="hoverItemStyle" class="device-layout-item"></div>-->
    <div
      :id="`layoutContainer-${widget.id}`"
      class="position-full"
      @dragenter="layoutActive = true"
      @dragleave="layoutActive = false"
      @drop="drop">
      <DeviceWidget
        v-for="child in widget.children"
        :key="child.id"
        :widget="child"
        :is-multi-mode="isMultiMode"
        :is-select-mode="isSelectMode"
        @dragging="draggingHandler"
        @dragStop="deviceWidgetDragStop"></DeviceWidget>
    </div>
    <!--关联设备-->
    <el-dialog
      title="关联设备"
      :visible.sync="relateDeviceDialog"
      :close-on-click-modal="false"
      width="500px"
      center
      :append-to-body="true"
      @close="relateDeviceDialog=false">
      <RelateDevice :devices="devices" @relateDeviceCancel="relateDeviceCancel"
                    @relateDeviceSubmit="relateDeviceSubmit"></RelateDevice>
    </el-dialog>
  </div>
</template>

<script>
  import {mapActions, mapState} from 'vuex';
  import {WidgetDeviceLayout} from "@/modules/IDE/widget/atom/widgetDeviceLayout";
  import DeviceWidget from "@/components/IDE/widget/atom/DeviceWidget.vue";
  import {WidgetFactory} from "@/modules/IDE/widgetFactory";
  import Widget from "@/components/IDE/widget/atom/Widget.vue";
  import API from "@/api";
  import {formatColor, messageHandle} from "@/utils";
  import {PageHandler} from "@/modules/IDE/pageHandler";
  import RelateDevice from "@/components/IDE/RelateDevice.vue";

  export default {
    name: 'DeviceWidgetLayout',
    components: {RelateDevice, Widget, DeviceWidget},
    props: {
      widget: {
        type: WidgetDeviceLayout,
        default: () => {
        }
      },
      isMultiMode: {
        type: Boolean,
        default: false
      },
      isSelectMode: {
        type: Boolean,
        default: false
      },
    },
    data() {
      return {
        layoutContainer: null,
        layoutActive: false,
        direction: 'horizontal',
        offsetX: 0,
        offsetY: 0,
        rows: 3,
        columns: 8,
        gapX: 16,
        gapY: 16,
        unitSize: 180,
        gridMatrix: [],
        dragOverHandler: () => {
        },
        draggingHandler: () => {
        },
        hoverGridItems: [],
        hoverGridItem: undefined,
        pageHdl: new PageHandler('page'),
        relateDeviceDialog: false,
        devices: [],
        newDevices: [],
        deviceComponentId: ''
      };
    },
    computed: {
      ...mapState({
        activeTheme: state => state.ide.activeTheme,
        draggingDeviceWidget: state => state.ide.draggingDeviceWidget,
        showWidgetPage: state => state.ide.showWidgetPage
      }),
      state() {
        if (!this.widget || !this.widget.UI.state) return;
        return this.widget.getCurrentState();
      },
      gridItemStyle() {
        return (gridItem = {}) => {
          const {row, column, left, top, width, height} = gridItem;
          const {UI = {}} = this.widget;
          const {gridColor, hoverColor} = UI.data;
          const style = {
            left: `${left}px`,
            top: `${top}px`,
            width: `${width}px`,
            height: `${height}px`
          };
          const isOccupied = this.occupiedGridItems.find(item => {
            return item.row === row && item.column === column;
          });
          const isHover = this.hoverGridItems.find(item => {
            return item.row === row && item.column === column;
          });
          if (isHover) {
            style.background = formatColor(hoverColor, this.activeTheme);
          } else {
            if (!isOccupied) {
              style.border = `2px ${formatColor(gridColor, this.activeTheme)} dashed`;
            }
          }
          return style;
        }
      },
      hoverItemStyle() {
        const {left, top, width, height} = this.hoverGridItem;
        return {
          left: `${left}px`,
          top: `${top}px`,
          width: `${width}px`,
          height: `${height}px`
        }
      },
      staticChildren() {
        const {children = []} = this.widget;
        return children.filter(child => !child.isDeleted && !child.isDragging);
      },
      // 计算当前已经被小组件占用了的网格集合
      occupiedGridItems() {
        const items = [];
        this.staticChildren.find(child => {
          const {widgetPosition = {}, widgetSize = {}} = child.UI.data;
          const {row, column} = widgetPosition;
          const {rows, columns} = widgetSize;
          for (let i = 0; i < rows; i++) {
            for (let j = 0; j < columns; j++) {
              items.push({
                row: row + i,
                column: column + j,
              });
            }
          }
        });
        return items;
      }
    },
    watch: {
      'widget.rect': {
        handler() {
          this.layoutResize();
        },
        deep: true
      },
      'widget.UI.data': {
        handler() {
          this.layoutResize();
        },
        deep: true
      },
      layoutActive(active = false) {
        if (!active) {
          this.hoverGridItems = [];
          this.hoverGridItem = undefined;
        }
      },
      draggingDeviceWidget() {
        if (!this.layoutActive) return;
        this.dragOverHandler();
      }
    },
    mounted() {
      this.initData();
      this.layoutContainer = document.getElementById(`layoutContainer-${this.widget.id}`);
      this.dragOverHandler = _.throttle(() => {
        if (!this.draggingDeviceWidget) return;
        // 获取布局组件相对窗口的位置
        const containerRect = this.layoutContainer.getBoundingClientRect();
        const {left: containerLeft, top: containerTop} = containerRect;
        // 获取当前拖拽的小组件相对窗口的位置
        const {clientX, clientY, widgetSize = {}} = this.draggingDeviceWidget;
        // 计算得出当前拖拽小组件相对布局组件的位置
        const relativeX = clientX - containerLeft;
        const relativeY = clientY - containerTop;
        this.calcHoverGridItems({x: relativeX, y: relativeY}, widgetSize);
      }, 250);
      this.draggingHandler = _.throttle((widget = {}) => {
        const {data = {}} = widget.UI || {};
        const {widgetSize = {}} = data;
        const {left, top, width, height} = widget.tempRect;
        this.layoutActive = true;
        this.calcHoverGridItems({x: left + Math.round(width / 2), y: top + Math.round(height / 2)}, widgetSize);
      }, 250);
    },
    beforeDestroy() {
      this.layoutContainer = null;
    },
    methods: {
      ...mapActions(['updateShowWidgetPage']),

      initData() {
        // 根据布局组件中的行数或列数、网格间距，计算出小组件的最小单位、列数或行数，左边和上边的偏移量
        const {tempRect = {}, UI = {}} = this.widget;
        const {width, height} = tempRect;
        const {direction, rows, columns, gapX, gapY} = UI.data;
        this.direction = direction;
        this.gapX = gapX;
        this.gapY = gapY;
        switch (direction) {
          case 'horizontal':
            this.rows = rows;
            this.unitSize = Math.floor((height - (rows - 1) * gapY) / rows);
            this.columns = Math.floor((width + gapX) / (this.unitSize + gapX));
            break;
          case 'vertical':
            this.columns = columns;
            this.unitSize = Math.floor((width - (columns - 1) * gapX) / columns);
            this.rows = Math.floor((height + gapY) / (this.unitSize + gapY));
            break;
          default:
            break;
        }
        this.offsetX = Math.floor((width - (this.unitSize + gapX) * this.columns + gapX) / 2);
        this.offsetY = Math.floor((height - (this.unitSize + gapY) * this.rows + gapY) / 2);
        UI.data.offsetX = this.offsetX;
        UI.data.offsetY = this.offsetY;
        UI.data.unitSize = this.unitSize;
        // 根据行数/列数、最小单位，组装网格辅助线数据源
        this.gridMatrix = [];
        for (let row = 0; row < this.rows; row++) {
          this.gridMatrix.push([]);
          for (let column = 0; column < this.columns; column++) {
            const left = (this.unitSize + this.gapX) * column + this.offsetX;
            const top = (this.unitSize + this.gapY) * row + this.offsetY;
            const offset = Math.floor(this.unitSize / 2);
            this.gridMatrix[row].push({
              row: row,
              column: column,
              left: left,
              top: top,
              width: this.unitSize,
              height: this.unitSize,
              center: {
                left: left + offset,
                top: top + offset
              }
            });
          }
        }
      },
      layoutResize() {
        this.initData();
        const {children = []} = this.widget;
        children.forEach(component => {
          const {widgetPosition = {}, widgetSize = {}} = component.UI.data;
          const {row, column} = widgetPosition;
          const {rows, columns} = widgetSize;
          const sourceRect = {
            width: component.rect.width,
            height: component.rect.height,
          };
          const targetRect = {
            width: this.unitSize * columns + (columns - 1) * this.gapX,
            height: this.unitSize * rows + (rows - 1) * this.gapY,
          };
          WidgetFactory.getWidgetFlattenChildren(component, false).forEach(child => {
            WidgetFactory.widgetScaleTransform(child, sourceRect, targetRect);
          });
          const gridItem = this.gridMatrix[row][column];
          if (gridItem) {
            component.rect.left = gridItem.left;
            component.rect.top = gridItem.top;
          }
        });
      },
      calcDistance(point1, point2) {
        const powX = Math.pow(point1.x - point2.x, 2);
        const powY = Math.pow(point1.y - point2.y, 2);
        return Math.round(Math.sqrt(powX + powY));
      },
      // 判断当前拖拽的小组件和布局组件的网格是否存在交集
      judgeIntersection(gridItem = {}, widgetCenter = {}, widgetSize = {}) {
        const {left, top, width, height} = gridItem;
        const {x, y} = widgetCenter;
        const {rows = 1, columns = 1} = widgetSize;
        const sizeWidth = this.unitSize * rows + (rows - 1) * this.gapX;
        const sizeHeight = this.unitSize * columns + (columns - 1) * this.gapY;
        const offsetX = Math.round(sizeWidth / 2);
        const offsetY = Math.round(sizeHeight / 2);
        const outOfLeft = left + width < x - offsetX;
        const outOfRight = left > x + offsetX;
        const outOfTop = top + height < y - offsetY;
        const outOfBottom = top > y + offsetY;
        if (!(outOfLeft || outOfRight || outOfTop || outOfBottom)) {
          const {left, top, width, height} = gridItem;
          const gridItemCenter = {x: left + width / 2, y: top + height / 2};
          // 如果存在交集，则记录网格与当前拖拽小组件的实时距离
          gridItem.distance = this.calcDistance(widgetCenter, gridItemCenter);
          return true;
        } else {
          gridItem.distance = null;
          return false;
        }
      },
      // 计算得出当前拖拽的小组件在布局组件中距离最近的网格是哪个，用于高亮处理
      calcHoverGridItems(widgetCenter = {}, widgetSize = {}) {
        const {rows = 1, columns = 1} = widgetSize;
        const intersections = [];
        for (let row = 0; row < this.rows; row++) {
          intersections.push([]);
          for (let column = 0; column < this.columns; column++) {
            const gridItem = this.gridMatrix[row][column];
            if (this.judgeIntersection(gridItem, widgetCenter, widgetSize)) {
              intersections[row].push(gridItem);
            }
          }
        }
        const destIntersections = intersections.filter(items => !!items.length);
        this.hoverGridItems = _.flatten(destIntersections);
        let bestDistance = Infinity;
        let bestResult = [];
        const subGrids = this.findAllSubGrids(destIntersections, rows, columns);
        // 在所有满足尺寸的子宫格集合中找到整体距离拖拽小组件最近的子宫格
        subGrids.forEach((grids = []) => {
          let curDistance = 0;
          grids.forEach(grid => curDistance += grid.distance);
          if (curDistance < bestDistance) {
            bestDistance = curDistance;
            bestResult = grids;
          }
        });
        this.hoverGridItems = bestResult;
        // 以最近子宫格的起始点作为 hoverGridItem，用于最终的坐标吸附
        this.hoverGridItem = this.hoverGridItems[0];
      },
      // 找出交集宫格中满足尺寸的所有子宫格
      findAllSubGrids(intersections, subGridRows, subGridCols) {
        if (!intersections.length) return [];
        const numRows = intersections.length;
        const numCols = intersections[0].length;
        const results = [];
        // 遍历每个可能的子宫格的起始点
        for (let startRow = 0; startRow <= numRows - subGridRows; startRow++) {
          for (let startCol = 0; startCol <= numCols - subGridCols; startCol++) {
            const subGrid = [];
            // 从起始点收集子宫格中的元素
            for (let rowOffset = 0; rowOffset < subGridRows; rowOffset++) {
              for (let colOffset = 0; colOffset < subGridCols; colOffset++) {
                const item = intersections[startRow + rowOffset][startCol + colOffset];
                subGrid.push(item);
              }
            }
            results.push(subGrid);
          }
        }
        return results;
      },
      drop(event) {
        event.preventDefault();
        this.dragOverHandler.cancel();
        //拖拽如果有设备，就要匹配关联设备 如果没有直接拖拽组件
        if (!this.draggingDeviceWidget) return
        this.deviceComponentId = this.draggingDeviceWidget._id
        if (this.draggingDeviceWidget.devices.length > 0) {
          this.devices = this.draggingDeviceWidget.devices
          this.relateDeviceDialog = true
        } else {
          const param = {roomId: this.widget.roomId, componentId: this.draggingDeviceWidget._id};
          this.creatWidget(param)
        }
      },
      creatWidget(param) {
        API.room.roomApplyComponent(param).then(resp => {
          const widgets = resp.data || [];
          if (!widgets.length) {
            messageHandle({type: 'warning', msg: `组件配置信息为空，请先进行组件制作！`});
            return;
          }
          // 先根据 widgets 数组组装递归结构
          const componentPages = WidgetFactory.assembleWidgetFromArray(widgets);
          const component = componentPages.find(page => !page.isAppendage);
          const appendages = componentPages.filter(page => page.isAppendage&& page.appendageId);
          const appendageDialog = componentPages.filter(page => page.isAppendage && page.appendDialogId);
          // 创建组件本身
          const createComponent = () => {
            // 对小组件进行缩放处理
            const sourceRect = {width: component.rect.width, height: component.rect.height};
            const {widgetSize = {}} = component.UI.data;
            const {rows = 1, columns = 1} = widgetSize;
            const targetRect = {
              width: this.unitSize * columns + (columns - 1) * this.gapX,
              height: this.unitSize * rows + (rows - 1) * this.gapY,
            };
            WidgetFactory.getWidgetFlattenChildren(component, false).forEach(child => {
              WidgetFactory.widgetScaleTransform(child, sourceRect, targetRect);
            });
            // 将网格的 rect 赋值给目标小组件
            const {left, top, row, column} = this.hoverGridItem;
            component.rect.left = left;
            component.rect.top = top;
            component.UI.data.widgetPosition = {row, column};
            WidgetFactory.widgetAppendChild(this.widget, component);
          };
          const createAppendages = (type,data) => {
            const fullPath = WidgetFactory.getWidgetFullPath(this.widget.id);
            const devicePage = WidgetFactory.getWidgetById(this.widget.id);
            const parentPage = WidgetFactory.getWidgetById(fullPath[0]);
            // 对附加内容进行缩放处理，并追加到当前组件从属的页面上
            const targetPage = type === 'detailPage' ? devicePage : parentPage;
            const targetRect = {width: targetPage.rect.width, height: targetPage.rect.height};
            data.forEach(appendage => {
              const sourceRect = {width: appendage.rect.width, height: appendage.rect.height};
              WidgetFactory.getWidgetFlattenChildren(appendage, false).forEach(child => {
                WidgetFactory.widgetScaleTransform(child, sourceRect, targetRect);
              });
              //全局弹框的类型需要改下 因为是小组件类型 默认加了dragLayout属性
              if(type!=='detailPage'){
                appendage.type='componentComposite'
              }
              appendage.display = false;
              appendage.rect.left =type==='detailPage'? -component.rect.left:0;
              appendage.rect.top =type==='detailPage'? -component.rect.top:0;
              WidgetFactory.widgetAppendChild(type==='detailPage'? component:parentPage, appendage);
            });
          };
          createComponent();
          // 创建组件详情页 把他添加到当前小组件下 位置就是当前小组件位置的负数，大小是当前拖拽布局大小一样
          createAppendages('detailPage', appendages);
          // 创建组件全局弹框 把他添加到根目录下
          createAppendages('dialog', appendageDialog);

          // TODO 撤销重做相关处理完善
          // 发送通知刷新全局设备列表和真值反馈规则列表
          this.$EventBus.$emit('refreshGlobalDevices');
          this.$EventBus.$emit('refreshGlobalLogicRules');
          this.$EventBus.$emit('deviceSortExecute');
        }).catch(err => {
          console.log('##### err #####', err);
        }).finally(() => {
          this.layoutActive = false;
        });
      },
      deviceWidgetDragStop(deviceWidget) {
        if (!this.hoverGridItem) return;
        const {left, top, row, column} = this.hoverGridItem;
        deviceWidget.rect = {
          left, top,
          width: deviceWidget.rect.width,
          height: deviceWidget.rect.height,
        };
        deviceWidget.UI.data.widgetPosition = {row, column};
        this.draggingHandler.cancel();
        this.layoutActive = false;
      },
      relateDeviceSubmit(val) {
        this.newDevices = val
        const param = {
          roomId: this.widget.roomId,
          componentId: this.deviceComponentId,
          isSync: false,
          newDevices: this.newDevices
        };
        this.creatWidget(param)
        this.relateDeviceDialog = false
      },
      relateDeviceCancel() {
        this.relateDeviceDialog = false
        this.layoutActive = false;
      }
    }
  };
</script>

<style lang="less" scoped>

  .device-layout-grid {
    position: absolute;
  }

  .device-layout-item {
    position: absolute;
    background: red;
    border-radius: 20px;
  }

  .snap-grid-item {
    position: absolute;
    border-radius: 20px;
  }

</style>
